package com.googlecode.androidkurskoblenz.cub3snake;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.googlecode.androidkurskoblenz.ledcube.CubeConnection;
import com.googlecode.androidkurskoblenz.ledcube.CubeInterface;
import com.googlecode.androidkurskoblenz.sockethelper.DeviceList;
import com.googlecode.androidkurskoblenz.sockethelper.ISocket;

public class MainActivity extends Activity implements CubeInterface, GameInterface
{
	private static final int DEFAULT_PORT = 54145;
	private static final String PREFS_NAME = "preferences";
	private static final int REQUEST_SELECT_DEVICE = 1;
	private static final long VIBRATE_STEP = 50;
	private static final long[] VIBRATE_STEP_FOOD =
	{
			0, 50, 50, 50, 50, 50, 50
	};
	private static final long VIBRATE_STOP = 1000;
	
	private Button btnStart;
	private CubeConnection cube = new CubeConnection();
	private AlertDialog dialogLoading, dialogReconnect, dialogWrongDimensions;
	private Game game;
	private TextView tv;
	private Vibrator vibrator;
	protected boolean forceReconnect;
	
	/*
	 * checks if there's a saved game and whether it has the same dimensions as the connected cube.
	 */
	private void checkGame()
	{
		if(game == null)
		{
			game = new Game(this, cube.getWidth(), cube.getDepth(), cube.getHeight());
			loadGame();
			return;
		}
		
		if(game.getWidth() == cube.getWidth() &&
				game.getDepth() == cube.getDepth() &&
				game.getHeight() == cube.getHeight())
			loadGame();
		else
			dialogWrongDimensions.show();
	}
	
	/*
	 * writes the complete state of the cube model into the physical cube
	 */
	private void loadGame()
	{
		if(cube == null || cube.isRunning() != CubeConnection.CubeStatus.Connected)
			return;
		
		String command;
		int x, y, z;
		for(z = 0; z < game.getHeight(); z++)
		{
			command = "L" + z + ",";
			
			for(y = 0; y < game.getDepth(); y++)
			{
				for(x = 0; x < game.getWidth(); x++)
				{
					command += game.getCellState(x, y, z) == Game.CellState.CLEAR ? "0" : "1";
				}
			}
			command += "\n";
			
			try
			{
				cube.send(command);
			}
			catch(CubeConnection.ConnectionError e)
			{}
			catch(CubeConnection.StatusException e)
			{}
		}
		
		SharedPreferences preferences = getPreferences(MODE_PRIVATE);
		game.setDifficulty(preferences.getInt("difficulty", 1));
		
		updateInfo();
	}
	
	/*
	 * called after the user has selected a connection to a cube (Bluetooth or Wi-Fi)
	 */
	private void onDeviceSelected(ISocket socket)
	{
		dialogLoading.show();
		try
		{
			cube.start(this, this, socket);
		}
		catch(CubeConnection.StatusException e)
		{}
	}
	
	/*
	 * creates a new Game with the dimensions of the currently connected cube
	 */
	private void reset()
	{
		game = new Game(this, cube.getWidth(), cube.getDepth(), cube.getHeight());
		loadGame();
	}
	
	/*
	 * asks the user to select a connection to a device. This is done by starting the DeviceList activity
	 */
	private void selectDevice()
	{
		Intent intent = new Intent(MainActivity.this, DeviceList.class);
		
		SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
		
		String host = settings.getString("last.host", "");
		int port = settings.getInt("last.port", DEFAULT_PORT);
		intent.putExtra("default.host", host);
		intent.putExtra("default.port", port);
		startActivityForResult(intent, REQUEST_SELECT_DEVICE);
	}
	
	/*
	 * sets the difficulty and stores it in the SharedPreferences
	 */
	private void setDifficulty(int difficulty)
	{
		if(game != null)
			game.setDifficulty(difficulty);
		
		SharedPreferences prefs = getPreferences(MODE_PRIVATE);
		Editor editor = prefs.edit();
		editor.putInt("difficulty", difficulty);
		editor.commit();
	}
	
	/*
	 * Updates the button label (Start/Pause/Reset) and the game status (Paused/Running/Stopped + earned points)
	 */
	private void updateInfo()
	{
		if(game == null)
			return;
		
		Game.State state = game.getState();
		String info = "";
		
		switch(state)
		{
			case PAUSED:
				info = "Paused";
				btnStart.setText("Start");
				break;
			case RUNNING:
				info = "Running";
				btnStart.setText("Pause");
				break;
			case STOPPED:
				info = "Stopped";
				btnStart.setText("Reset");
				break;
			default:
				info = "";
		}
		
		info += "\nPoints: " + game.getPoints();
		
		tv.setText(info);
	}
	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data)
	{
		super.onActivityResult(requestCode, resultCode, data);
		
		if(requestCode == REQUEST_SELECT_DEVICE)
		{
			if(resultCode == RESULT_OK)
			{
				data.putExtra("bluetooth.uuid", CubeConnection.uuid.toString());
				
				if(data.getStringExtra("type").equals("network"))
				{
					SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
					Editor editor = settings.edit();
					editor.putString("last.host", data.getStringExtra("network.host"));
					editor.putInt("last.port", data.getIntExtra("network.port", DEFAULT_PORT));
					editor.commit();
				}
				
				ISocket socket = DeviceList.getSocket(data);
				onDeviceSelected(socket);
			}
			else
			{
				finish();
				return;
			}
		}
	}
	
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		
		setContentView(R.layout.activity_main);
		
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage(R.string.title_reconnect);
		builder.setTitle(R.string.title_connection_closed);
		builder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener()
		{
			public void onClick(DialogInterface dialog, int id)
			{
				selectDevice();
			}
		});
		builder.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener()
		{
			public void onClick(DialogInterface dialog, int id)
			{
				dialog.cancel();
			}
		});
		builder.setOnCancelListener(new DialogInterface.OnCancelListener()
		{
			@Override
			public void onCancel(DialogInterface dialog)
			{
				finish();
			}
		});
		dialogReconnect = builder.create();
		
		builder = new AlertDialog.Builder(this);
		builder.setTitle(R.string.title_loading);
		builder.setCancelable(false);
		builder.setView(getLayoutInflater().inflate(R.layout.dialog_progressbar, null));
		dialogLoading = builder.create();
		
		builder = new AlertDialog.Builder(this);
		builder.setTitle(R.string.title_wrong_dimensions);
		builder.setMessage(R.string.msg_wrong_dimensions);
		builder.setPositiveButton(R.string.title_select_other, new DialogInterface.OnClickListener()
		{
			public void onClick(DialogInterface dialog, int id)
			{
				try
				{
					forceReconnect = true;
					cube.stop();
				}
				catch(CubeConnection.StatusException e)
				{}
			}
		});
		builder.setNeutralButton(R.string.title_new_game, new DialogInterface.OnClickListener()
		{
			public void onClick(DialogInterface dialog, int id)
			{
				game = new Game(MainActivity.this, cube.getWidth(), cube.getDepth(), cube.getHeight());
				loadGame();
			}
		});
		builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener()
		{
			public void onClick(DialogInterface dialog, int id)
			{
				dialog.cancel();
			}
		});
		builder.setOnCancelListener(new DialogInterface.OnCancelListener()
		{
			@Override
			public void onCancel(DialogInterface dialog)
			{
				finish();
			}
		});
		dialogWrongDimensions = builder.create();
		
		vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
		
		selectDevice();
		
		tv = (TextView) findViewById(R.id.textViewState);
		btnStart = (Button) findViewById(R.id.button_start);
	}
	
	@Override
	protected void onDestroy()
	{
		super.onDestroy();
		
		try
		{
			cube.stop();
		}
		catch(CubeConnection.StatusException e)
		{}
	}
	
	@Override
	protected void onPause()
	{
		super.onPause();
		if(game != null)
		{
			try
			{
				game.pause();
			}
			catch(Exception e)
			{}
		}
	}
	
	@Override
	protected void onStart()
	{
		super.onStart();
		
		SharedPreferences preferences = getPreferences(MODE_PRIVATE);
		
		String str = preferences.getString("savedgame", null);
		if(str != null)
		{
			try
			{
				game = new Game(this, str);
				loadGame();
			}
			catch(UnsupportedOperationException e)
			{}
			
			SharedPreferences.Editor editor = preferences.edit();
			editor.remove("savedgame");
			editor.commit();
		}
	}
	
	@Override
	protected void onStop()
	{
		super.onStop();
		
		if(game != null && game.getState() != Game.State.STOPPED)
		{
			SharedPreferences preferences = getPreferences(MODE_PRIVATE);
			SharedPreferences.Editor editor = preferences.edit();
			try
			{
				editor.putString("savedgame", new String(game.serialize()));
			}
			catch(UnsupportedOperationException e)
			{}
			editor.commit();
		}
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu)
	{
		// TODO change icon
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.activity_main, menu);
		
		SharedPreferences preferences = getPreferences(MODE_PRIVATE);
		MenuItem item;
		switch(preferences.getInt("difficulty", 1))
		{
			case 0:
				item = menu.findItem(R.id.menu_item_easy);
				break;
			case 1:
				item = menu.findItem(R.id.menu_item_medium);
				break;
			case 2:
				item = menu.findItem(R.id.menu_item_difficult);
				break;
			default:
				return true;
		}
		item.setChecked(true);
		
		return true;
	}
	
	@Override
	public void onCubeConnect(CubeConnection cube)
	{
		dialogLoading.dismiss();
		
		checkGame();
	}
	
	@Override
	public void onCubeDisconnect(CubeConnection cube)
	{
		try
		{
			if(game != null)
				game.pause();
		}
		catch(Exception e)
		{}
		
		dialogLoading.hide();
		dialogReconnect.hide();
		
		if(forceReconnect)
		{
			forceReconnect = false;
			selectDevice();
			dialogReconnect.hide();
		}
		else
			dialogReconnect.show();
	}
	
	@Override
	public void onCubeError(CubeConnection cube)
	{}
	
	@Override
	public void onCubeReceive(CubeConnection cubeConnection, String data)
	{}
	
	/*
	 * called when one of the directional buttons is clicked. This methods determines which one has been clicked.
	 */
	public void onDirectionClick(View view)
	{
		Game.Direction direction;
		
		switch(view.getId())
		{
			case R.id.button_back:
				direction = Game.Direction.BACKWARD;
				break;
			case R.id.button_down:
				direction = Game.Direction.DOWN;
				break;
			case R.id.button_front:
				direction = Game.Direction.FORWARD;
				break;
			case R.id.button_left:
				direction = Game.Direction.LEFT;
				break;
			case R.id.button_right:
				direction = Game.Direction.RIGHT;
				break;
			case R.id.button_up:
				direction = Game.Direction.UP;
				break;
			default:
				return;
		}
		
		game.setDirection(direction);
	}
	
	@Override
	public void onGameCellChange(byte x, byte y, byte z, Game.CellState state)
	{
		try
		{
			cube.setLED(x, y, z, (byte) (state == Game.CellState.CLEAR ? 0 : 1));
		}
		catch(CubeConnection.ConnectionError e)
		{}
		catch(CubeConnection.StatusException e)
		{}
	}
	
	@Override
	public void onGameStateChange(Game.State state)
	{
		updateInfo();
	}
	
	@Override
	public void onGameStep(boolean food)
	{
		updateInfo();
		
		if(food)
			vibrator.vibrate(VIBRATE_STEP_FOOD, -1);
		else
			vibrator.vibrate(VIBRATE_STEP);
	}
	
	@Override
	public void onGameStop()
	{
		vibrator.vibrate(VIBRATE_STOP);
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item)
	{
		switch(item.getItemId())
		{
			case R.id.menu_item_disconnect:
				try
				{
					cube.stop();
				}
				catch(CubeConnection.StatusException e)
				{}
				return true;
				
			case R.id.menu_item_easy:
				setDifficulty(0);
				item.setChecked(true);
				return true;
			case R.id.menu_item_medium:
				setDifficulty(1);
				item.setChecked(true);
				return true;
			case R.id.menu_item_difficult:
				setDifficulty(2);
				item.setChecked(true);
				return true;
				
			default:
				return super.onOptionsItemSelected(item);
		}
	}
	
	/*
	 * called when the start button is clicked. Depending on the current game state, this buttons starts, pauses or resets
	 * the game.
	 */
	public void onStartClick(View view)
	{
		try
		{
			switch(game.getState())
			{
				case PAUSED:
					game.start();
					break;
				case RUNNING:
					game.pause();
					break;
				default:
					reset();
			}
		}
		catch(Exception e)
		{}
	}
}
